home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Plus 2002 #11
/
Amiga Plus CD - 2002 - No. 11.iso
/
Tools
/
Freeware
/
sysmon
/
src
/
sysinfo
/
SysInfo.c
< prev
next >
Wrap
C/C++ Source or Header
|
2002-10-27
|
12KB
|
344 lines
/*
** $RCSfile: sysinfo.c,v $
** $Filename: sysinfo.c $
** $Revision: 1.0 $
** $Date: 1996/08/20 15:35:37 $
**
** sysinfo.library interface to sysmon.library features (version 2.2)
** See the SysInfo.doc autodoc file in the Executive distribution
** for more information about the available function calls.
**
** (C) Copyright 1995-2001 by Etienne Vogt <Etienne.Vogt@obspm.fr>
** SysInfo API by Petri Nordlund <petrin@megabaud.fi>
*/
#include <exec/libraries.h>
#include <exec/memory.h>
#include <exec/tasks.h>
#include <exec/alerts.h>
#include <exec/execbase.h>
#include <devices/timer.h>
#define __USE_SYSBASE
#include <proto/exec.h>
#include <proto/timer.h>
#include <clib/alib_protos.h>
#include "/sysmon.h"
#include "/sysmon_protos.h"
#include "/sysmon_pragmas.h"
#include "sysinfo.h"
#define CPU_USAGE_RECENT 60 /* Recent CPU Usage is last minute */
static volatile ULONG RecentCPUUsed[CPU_USAGE_RECENT];
static volatile int LastRecentCPU = 0;
static volatile ULONG LastSecDispCount, LastSecVolTSw, LastSecInvTSw;
struct SysmonBase *SysmonBase = NULL;
struct Device *TimerBase = NULL;
struct ExecBase *SysBase = NULL;
static struct timerequest timereq;
static BOOL ServerRunning = FALSE, ServerExit = FALSE;
static struct Task *ServerTask;
extern __far void ServerEntry(void); /* Stubs for server task */
extern __far struct Library *SysInfoBase;
int __saveds __asm __UserLibInit(register __a6 struct Library *libbase);
int __saveds __asm __UserLibCleanup(register __a6 struct Library *libbase);
static struct EClockVal subEClockVal(struct EClockVal e2, struct EClockVal e1);
static __inline struct EClockVal addEClockVal(struct EClockVal e2, struct EClockVal e1);
void __saveds SysInfoServer(void);
/* This function is executed by ramlib as part of the library initialization process.
We open sysmon.library, timer.device and start the server task via an assembler
stub routine as CreateTask() does not allow us to preload A6 with the library
base (and AddTask() is just a pain to use directly). */
int __saveds __asm __UserLibInit(register __a6 struct Library *libbase)
{ int rc = 0;
SysBase = *(struct ExecBase **)4;
if (SysmonBase = (struct SysmonBase *)OpenLibrary("sysmon.library",1))
{ if (OpenDevice("timer.device",UNIT_VBLANK,(struct IORequest *)&timereq,0) == 0)
{ TimerBase = timereq.tr_node.io_Device;
Forbid();
SysInfoBase = libbase;
if (ServerTask = CreateTask("SysInfo.server",18,ServerEntry,4000))
{ ServerRunning = TRUE;
ServerTask->tc_UserData = libbase;
}
else rc = 1;
Permit();
}
else
{ CloseLibrary((struct Library *)SysmonBase);
Alert(AT_Recovery|AG_OpenDev|AO_TimerDev);
rc = 1;
}
}
else rc = 1;
return rc;
}
/* This routine is called by ramlib's low memory handler on a failed memory allocation
request if our library is no longer opened. Since we can't task switch from a low mem
handler, we notify the server task that it should exit as soon as possible and delay
the expunge. The task will call this again on exit to actually flush the library out. */
int __saveds __asm __UserLibCleanup(register __a6 struct Library *libbase)
{ if (ServerRunning) /* If server is running, tell it to exit and delay the expunge */
{ ServerExit = TRUE;
return 1;
}
else
{ if (TimerBase) CloseDevice((struct IORequest *)&timereq);
if (SysmonBase) CloseLibrary((struct Library *)SysmonBase);
return 0;
}
}
*/ This function is used to query the library about its supported features */
__saveds struct SysInfo *InitSysInfo(void)
{ struct SysInfo *si;
if (si = AllocMem(sizeof(struct SysInfo), MEMF_PUBLIC | MEMF_CLEAR))
{ si->loadavg_type = LOADAVG_FIXEDPNT;
si->loadavg_time1 = 60;
si->loadavg_time2 = 5*60;
si->loadavg_time3 = 15*60;
si->fscale = 60;
si->cpu_usage_implemented = CPU_USAGEF_TOTAL_IMPLEMENTED |
CPU_USAGEF_LASTSEC_IMPLEMENTED | CPU_USAGEF_RECENT_IMPLEMENTED |
CPU_USAGEF_TOTALCSW_IMPLEMENTED | CPU_USAGEF_IVVOCSW_IMPLEMENTED |
CPU_USAGEF_IVVOCSW_LASTSEC_IMPLEMENTED | CPU_USAGEF_TOTALCSW_LASTSEC_IMPLEMENTED;
si->task_cpu_usage_implemented = TASK_CPU_USAGEF_TOTAL_IMPLEMENTED |
TASK_CPU_USAGEF_TOTALCSW_IMPLEMENTED | TASK_CPU_USAGEF_IVVOCSW_IMPLEMENTED;
}
return si;
}
*/ The function cleans up whatever was allocated by InitSysInfo() */
__asm __saveds void FreeSysInfo(register __a0 struct SysInfo *si)
{ if (si) FreeMem(si, sizeof(struct SysInfo));
}
*/ This function computes Load Averages from the data collected by sysmon.library's
VBLANK interrupt server */
__asm __saveds void GetLoadAverage(register __a0 struct SysInfo *si, register __a1 struct SI_LoadAverage *la)
{ UWORD bufpos = SysmonBase->sb_LdAvrPtr;
ULONG total1=0, total2=0, total3=0;
UWORD count=0;
UBYTE bufval;
do
{ bufval = SysmonBase->sb_LdAvrBuffer[bufpos];
if (count < 60) total1 += bufval;
if (count < 5*60) total2 += bufval;
total3 += bufval;
if (bufpos == 0) bufpos = 15*60;
} while (bufpos--, count++, count < 15*60);
la->lavg_fixed.load1 = total1;
la->lavg_fixed.load2 = total2 / 5;
la->lavg_fixed.load3 = total3 / 15;
}
/* This function returns the Process ID associated to a task. Since we don't
support PIDs, we just return the TCB address. */
__asm __saveds LONG GetPid(register __a0 struct SysInfo *si)
{ return (LONG)FindTask(NULL);
}
/* This function returns the Process ID of the task's parent. We don't have
this information available, so we return an error condition. */
__asm __saveds LONG GetPpid(register __a0 struct SysInfo *si)
{ return -1L;
}
/* This function returns the Process group number associated to a task. Since
we don't support process groups, we return an error condition. */
__asm __saveds LONG GetPgrp(register __a0 struct SysInfo *si)
{ return -1L;
}
/* This function returns the so called nice value of a task. We don't support
such unixisms, so you will get an error */
__asm __saveds LONG GetNice(register __a0 struct SysInfo *si, register __d0 LONG which, register __d1 LONG who)
{ si->errno = WHICH_EINVAL;
return -1L;
}
/* This function sets the so called nice value of a task. We don't support
such unixisms, so you will get an error */
__asm __saveds LONG SetNice(register __a0 struct SysInfo *si, register __d0 LONG which, register __d1 LONG who, register __d2 LONG nice)
{ si->errno = WHICH_EINVAL;
return -1L;
}
/* This function allows you to request a notification when internal stats have
been updated. This is currently not supported. */
__asm __saveds struct SI_Notify *AddNotify(register __a0 struct SysInfo *si, register __d0 WORD flags, register __d1 LONG safety_limit)
{ return NULL;
}
/* This function removes a notification request. Since notifications are currently
not supported, there is nothing to do here. */
__asm __saveds void RemoveNotify(register __a0 struct SysInfo *si, register __a1 struct SI_Notify *notify)
{
}
/* This function returns statistics about CPU usage and task switches gathered by the
server task every second. */
__asm __saveds void GetCpuUsage(register __a0 struct SysInfo *si, register __a1 struct SI_CpuUsage *usage)
{ struct EClockVal uptime;
ULONG clockrate, RecentCPU=0;
int i;
clockrate = ReadEClock(&uptime);
usage->total_used_cputime = (SysmonBase->sb_CPUTime.ev_hi << 20 | SysmonBase->sb_CPUTime.ev_lo >> 12) / (clockrate >> 12);
usage->total_elapsed_time = (uptime.ev_hi << 20 | uptime.ev_lo >> 12) / (clockrate >> 12);
usage->total_csw = SysBase->DispCount;
usage->voluntary_csw = SysmonBase->sb_VolTSw;
usage->involuntary_csw = SysmonBase->sb_InvTSw;
Forbid();
i = (LastRecentCPU == 0 ? CPU_USAGE_RECENT-1 : LastRecentCPU-1);
usage->used_cputime_lastsec = RecentCPUUsed[i];
for (i = 0; i < CPU_USAGE_RECENT; i++) RecentCPU += RecentCPUUsed[i];
usage->recent_used_cputime = RecentCPU / CPU_USAGE_RECENT;
usage->involuntary_csw_lastsec = LastSecInvTSw;
usage->voluntary_csw_lastsec = LastSecVolTSw;
usage->total_csw_lastsec = LastSecDispCount;
Permit();
usage->used_cputime_lastsec_hz = clockrate;
usage->recent_used_cputime_hz = clockrate;
usage->recent_seconds = CPU_USAGE_RECENT;
}
/* This function returns total CPU usage and task switches for a given task
as maintained by sysmon.library. No last second or recent information is
maintained as this would be CPU expensive. */
__asm __saveds LONG GetTaskCpuUsage(register __a0 struct SysInfo *si, register __a1 struct SI_TaskCpuUsage *usage, register __a2 struct Task *task)
{ struct TaskInfo *tinfo;
struct EClockVal uptime, elapsed;
ULONG clockrate;
clockrate = ReadEClock(&uptime) >> 12;
if (tinfo = smGetTaskInfo(task))
{ usage->total_used_cputime = tinfo->ti_CPUTime.ev_hi << 20 | tinfo->ti_CPUTime.ev_lo >> 12;
usage->total_used_time_hz = clockrate;
elapsed = subEClockVal(uptime, tinfo->ti_StartTime);
usage->total_elapsed_time = (elapsed.ev_hi << 20 | elapsed.ev_lo >> 12) / clockrate;
usage->total_csw = tinfo->ti_DispCount;
usage->voluntary_csw = tinfo->ti_VolTSw;
usage->involuntary_csw = tinfo->ti_InvTSw;
return 0;
}
else
{ si->errno = WHICH_EINVAL;
return 1;
}
}
/* Some functions to add/subtract 64bit EClockVals */
static __inline struct EClockVal subEClockVal(struct EClockVal e2, struct EClockVal e1)
{ BOOL carry = (e1.ev_lo > e2.ev_lo);
e2.ev_lo -= e1.ev_lo;
e2.ev_hi -= e1.ev_hi;
if (carry) e2.ev_hi -= 1;
return e2;
}
static __inline struct EClockVal addEClockVal(struct EClockVal e2, struct EClockVal e1)
{ BOOL carry = (e2.ev_lo + e1.ev_lo < e2.ev_lo);
e2.ev_lo += e1.ev_lo;
e2.ev_hi += e1.ev_hi;
if (carry) e2.ev_hi += 1;
return e2;
}
/* The SysInfo.server task that will track CPU Usage and task switches every second
Note that we need to start this via an assembler stub routine as SAS/C libcode
option assumes the library base to be in A6 at function entry */
void __saveds SysInfoServer(void)
{ struct MsgPort *timerport;
struct Library *libbase = (struct Library *)FindTask(NULL)->tc_UserData;
struct EClockVal LastCPUUsage;
ULONG LastDispCount, LastVolTSw, LastInvTSw;
Forbid();
LastCPUUsage = SysmonBase->sb_CPUTime;
LastDispCount = SysBase->DispCount;
LastVolTSw = SysmonBase->sb_VolTSw;
LastInvTSw = SysmonBase->sb_InvTSw;
Permit();
if (timerport = CreateMsgPort())
{ timereq.tr_node.io_Message.mn_ReplyPort = timerport;
do
{ timereq.tr_time.tv_secs = 1;
timereq.tr_time.tv_micro = 0;
smScheduleWakeUp(&timereq);
do
{ smHibernate();
} while (GetMsg(timerport) != (struct Message *)&timereq);
Forbid(); /* Compute and store last second CPU Usage and task switches */
RecentCPUUsed[LastRecentCPU++] = subEClockVal(SysmonBase->sb_CPUTime,LastCPUUsage).ev_lo;
LastSecDispCount = SysBase->DispCount - LastDispCount;
LastSecVolTSw = SysmonBase->sb_VolTSw - LastVolTSw;
LastSecInvTSw = SysmonBase->sb_InvTSw - LastInvTSw;
LastCPUUsage = SysmonBase->sb_CPUTime;
LastDispCount = SysBase->DispCount;
LastVolTSw = SysmonBase->sb_VolTSw;
LastInvTSw = SysmonBase->sb_InvTSw;
Permit();
if (LastRecentCPU == CPU_USAGE_RECENT) LastRecentCPU = 0;
} while (!ServerExit);
DeleteMsgPort(timerport);
}
Forbid();
ServerRunning = FALSE;
if (libbase->lib_OpenCnt == 0 && libbase->lib_Flags & LIBF_DELEXP)
{ libbase->lib_OpenCnt++; /* Fake a last opener */
CloseLibrary(libbase); /* LibClose will do the delayed expunge */
}
}